package br.com.simtecnologia.util;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;

import net.vidageek.mirror.dsl.Mirror;

/**
 * A {@link Serializable} object value to wrapper one {@link Method} and make
 * it serializable as well.
 * @author Tomaz Lavieri
 */
public class MethodSerializable implements Serializable {
	private static final long serialVersionUID = -6064291575678213800L;
	private transient Method method;
	public MethodSerializable(Method method) {
		this.method = method;
	}
	
	/**
	 * Return the {@link Method} wrapped.
	 */
	public Method getMethod() {
		return method;
	}
	
	private void writeObject(ObjectOutputStream os) throws IOException {
		os.defaultWriteObject();
		boolean isNull = method == null;
		os.writeBoolean(isNull);
		if (isNull)	
			return;
		
		boolean isWithOutParameters = method.getParameterTypes().length == 0; 
		os.writeBoolean(isWithOutParameters);
		os.writeObject(method.getDeclaringClass());
		os.writeObject(method.getName());
		if (!isWithOutParameters)
			os.writeObject(method.getParameterTypes());
	}
	
	private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
		is.defaultReadObject();
		boolean isNull = is.readBoolean();
		if (isNull) 
			return;
		
		boolean isWithOutParameters = is.readBoolean();
		Class<?> targetClass = (Class<?>)is.readObject();
		String methodName = (String)is.readObject();
		if (isWithOutParameters)
			method = new Mirror().on(targetClass).reflect().method(methodName).withoutArgs();
		else
			method = new Mirror().on(targetClass).reflect().method(methodName).withArgs((Class<?>[]) is.readObject());
	}

	@Override
	public int hashCode() {
		if (getMethod() == null)
			return super.hashCode();
		final int prime = 31;
		int result = 1;
		result = prime * result + getMethod().hashCode();
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (getMethod() == null)
			return false;
		if (!(obj instanceof MethodSerializable))
			return false;
		MethodSerializable other = (MethodSerializable) obj;
		return getMethod().equals(other.getMethod());
	}
	
	
}
